package com.nf.db.handlers;

import com.nf.db.handlers.property.LocalDatePropertyHandler;

import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

/*
 * DefaultRowProcessor 实现了RowProcessor接口，提供了默认的toArray、toMap、toBean、toScalar方法。
 * 其中toArray方法直接将结果集中的每一行数据转换为Object[]数组，toMap方法将结果集中的每一行数据转换为Map<String, Object>，
 * toBean方法将结果集中的每一行数据转换为指定类型的对象，toScalar方法将结果集中的指定列或索引的数据转换为指定类型。
 * */
public class DefaultRowProcessor implements RowProcessor {
    // 这里的LocalDatePropertyHandler是自定义的日期类型转换器
    private static ServiceLoader<PropertyHandler> loader = ServiceLoader.load(PropertyHandler.class);
    /*
     * 把每一行数据转换为Object[]数组，其中数组的长度为列数，数组的元素为每一列的值
     * */
    @Override
    public Object[] toArray(ResultSet rs) throws SQLException {
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        Object[] obj = new Object[columnCount];
        for (int i = 0; i < columnCount; i++) {
            String columnName = metaData.getColumnName(i + 1);
            Object value = rs.getObject(columnName);
            obj[i] = value;
        }
        return obj;
    }

    /*
     * 把每一行数据转换为Map<String, Object>,其中key为列名，value为列值
     * */
    @Override
    public Map<String, Object> toMap(ResultSet rs) throws SQLException {
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        Map<String, Object> row = new HashMap<String, Object>();
        for (int i = 0; i < columnCount; i++) {
            String columnName = metaData.getColumnName(i + 1);
            Object value = rs.getObject(columnName);
            row.put(columnName, value);
        }
        return row;
    }

    /*
     * 调用newInstance，fillBean方法来处理结果集，把每一行数据填充到实体类中
     * */
    @Override
    public <T> T toBean(ResultSet rs, Class<T> clazz) throws SQLException {
        //要求实体类这个bean必须有默认构造函数
        Object bean = newInstance(clazz);
        fillBean(rs, bean);
        return (T) bean;
    }

    /*
     * 获取指定列的名字或列的索引的数据
     * */
    @Override
    public <T> T toScalar(ResultSet rs, String columnName, Integer columnIndex) throws SQLException {
        Object value = columnName == null ? rs.getObject(columnIndex) : rs.getObject(columnName);
        return (T) value;
    }

    /*
     * 创建实体类的实例
     * */
    private Object newInstance(Class<?> beanClz) {
        Object bean;
        try {
            //这里使用反射来创建实例
            bean = beanClz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            //这种catch写法是jdk的新特性，等价于分别写2个catch
            throw new RuntimeException(e);
        }
        return bean;
    }

    /*
     * 用来处理resultSet中的每一行数据，把每一行数据填充到实体类中
     * */
    private void fillBean(ResultSet rs, Object bean) throws SQLException {

        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        for (int i = 0; i < columnCount; i++) {
            //得到结果集的各个列的名字,最好用别名.如果sql语句中没有别名getColumnLabel效果等价于getColumnName
            String columnName = metaData.getColumnLabel(i + 1);
            //当前这个列对应的值
            Object value = rs.getObject(i + 1);
            //如果这个列对应的值是日期类型，则需要进行转换
            value = DatetoLocaltDate(value);
            //依据列名找到实体类对应的字段，并把value赋值给字段
            setFieldValue(bean, columnName, value);
        }
    }

    private void setFieldValue(Object bean, String columnName, Object value) {
        try {
            // 这里的getDeclaredField方法是反射的关键方法，可以获取到指定类的指定字段，包括私有字段
            Field field = bean.getClass().getDeclaredField(columnName);
            field.setAccessible(true);
            // 把value赋值给字段
            field.set(bean, value);
            field.setAccessible(false);
        } catch (NoSuchFieldException e) {
            // System.out.println("找不到字段，什么都不用干，相当于跳过了这个字段的后续赋值的处理");
            // 这里的catch没有用 |写法，因为不同的异常处理逻辑是不一样的
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private static Object DatetoLocaltDate(Object value) {
        if (value instanceof Date) {
            // 遍历所有实现了PropertyHandler接口的类，找到LocalDatePropertyHandler类
            for (PropertyHandler localDatePropertyHandler : loader) {
                value = localDatePropertyHandler.Parse((Date) value);
            }
        }
        return value;
    }
}
